From 8c7a8e9314f4cdef015611a9d9cad2fd21d4f2b4 Mon Sep 17 00:00:00 2001 From: William Jon McCann Date: Mon, 29 Jul 2013 12:06:20 -0400 Subject: [PATCH] Add autoscroll when dragging past boundary of range A problem with the zoom scroll mode is that you have to restart if you hit the bottom of the screen before you hit the bottom of your document. This commit adds an autoscroll feature to the zoom scroll: if you move outside the window while in zoom scroll mode, we keep scrolling in the direction you were going until you let go of the mouse button. https://bugzilla.gnome.org/show_bug.cgi?id=704703 --- gtk/gtkrange.c | 132 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 13 deletions(-) diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c index f99c309cb2..aff4ff3eaa 100644 --- a/gtk/gtkrange.c +++ b/gtk/gtkrange.c @@ -64,6 +64,7 @@ #define TIMEOUT_INITIAL 500 #define TIMEOUT_REPEAT 50 #define ZOOM_HOLD_TIME 500 +#define AUTOSCROLL_FACTOR 20 typedef struct _GtkRangeStepTimer GtkRangeStepTimer; @@ -116,7 +117,7 @@ struct _GtkRangePrivate gint n_marks; gint round_digits; /* Round off value to this many digits, -1 for no rounding */ gint slide_initial_slider_position; - gint slide_initial_coordinate; + gint slide_initial_coordinate_delta; gint slider_start; /* Slider range along the long dimension, in widget->window coords */ gint slider_end; @@ -146,6 +147,8 @@ struct _GtkRangePrivate guint zoom : 1; guint zoom_set : 1; GtkPressAndHold *press_and_hold; + GtkScrollType autoscroll_mode; + guint autoscroll_id; /* Fill level */ guint show_fill_level : 1; @@ -2536,8 +2539,8 @@ gtk_range_key_press (GtkWidget *widget, stop_scrolling (range); update_slider_position (range, - priv->slide_initial_coordinate, - priv->slide_initial_coordinate); + priv->slide_initial_coordinate_delta + priv->slide_initial_slider_position, + priv->slide_initial_coordinate_delta + priv->slide_initial_slider_position); return TRUE; } @@ -2732,12 +2735,12 @@ gtk_range_button_press (GtkWidget *widget, if (priv->orientation == GTK_ORIENTATION_VERTICAL) { priv->slide_initial_slider_position = priv->slider.y; - priv->slide_initial_coordinate = event->y; + priv->slide_initial_coordinate_delta = event->y - priv->slider.y; } else { priv->slide_initial_slider_position = priv->slider.x; - priv->slide_initial_coordinate = event->x; + priv->slide_initial_coordinate_delta = event->x - priv->slider.x; } range_grab_add (range, device, MOUSE_SLIDER, event->button); @@ -2770,11 +2773,6 @@ update_slider_position (GtkRange *range, gdouble zoom; gint i; - if (priv->orientation == GTK_ORIENTATION_VERTICAL) - delta = mouse_y - priv->slide_initial_coordinate; - else - delta = mouse_x - priv->slide_initial_coordinate; - if (priv->zoom) { zoom = MIN(1.0, (priv->orientation == GTK_ORIENTATION_VERTICAL ? @@ -2789,6 +2787,20 @@ update_slider_position (GtkRange *range, else zoom = 1.0; + /* recalculate the initial position from the current position */ + if (priv->slide_initial_slider_position == -1) + { + if (priv->orientation == GTK_ORIENTATION_VERTICAL) + priv->slide_initial_slider_position = (zoom * (mouse_y - priv->slide_initial_coordinate_delta) - priv->slider.y) / (zoom - 1.0); + else + priv->slide_initial_slider_position = (zoom * (mouse_x - priv->slide_initial_coordinate_delta) - priv->slider.x) / (zoom - 1.0); + } + + if (priv->orientation == GTK_ORIENTATION_VERTICAL) + delta = mouse_y - (priv->slide_initial_coordinate_delta + priv->slide_initial_slider_position); + else + delta = mouse_x - (priv->slide_initial_coordinate_delta + priv->slide_initial_slider_position); + c = priv->slide_initial_slider_position + zoom * delta; new_value = coord_to_value (range, c); @@ -2813,11 +2825,67 @@ update_slider_position (GtkRange *range, &handled); } +static void +remove_autoscroll (GtkRange *range) +{ + if (range->priv->autoscroll_id) + { + gtk_widget_remove_tick_callback (GTK_WIDGET (range), + range->priv->autoscroll_id); + range->priv->autoscroll_id = 0; + } + + /* unset initial position so it can be calculated */ + range->priv->slide_initial_slider_position = -1; + + range->priv->autoscroll_mode = GTK_SCROLL_NONE; +} + +static gboolean +autoscroll_cb (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer data) +{ + GtkRange *range; + gdouble increment; + gdouble value; + gboolean handled; + + range = GTK_RANGE (data); + + increment = gtk_adjustment_get_step_increment (range->priv->adjustment) / AUTOSCROLL_FACTOR; + if (range->priv->autoscroll_mode == GTK_SCROLL_STEP_BACKWARD) + increment *= -1; + + value = gtk_adjustment_get_value (range->priv->adjustment); + value += increment; + g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, value, + &handled); + + return G_SOURCE_CONTINUE; +} + +static void +add_autoscroll (GtkRange *range) +{ + GtkRangePrivate *priv = range->priv; + + if (priv->autoscroll_id != 0 + || priv->autoscroll_mode == GTK_SCROLL_NONE) + return; + + priv->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (range), + (GtkTickCallback)autoscroll_cb, + range, + NULL); +} + static void stop_scrolling (GtkRange *range) { range_grab_remove (range); gtk_range_remove_step_timer (range); + remove_autoscroll (range); } static gboolean @@ -2873,13 +2941,12 @@ gtk_range_button_release (GtkWidget *widget, { if (priv->grab_location == MOUSE_SLIDER) { - update_slider_position (range, priv->mouse_x, priv->mouse_y); if (priv->press_and_hold) gtk_press_and_hold_process_event (priv->press_and_hold, (GdkEvent *)event); } stop_scrolling (range); - + return TRUE; } @@ -2962,6 +3029,43 @@ gtk_range_scroll_event (GtkWidget *widget, return TRUE; } +static void +update_autoscroll_mode (GtkRange *range) +{ + GtkScrollType mode = GTK_SCROLL_NONE; + + if (range->priv->zoom) + { + GtkAllocation allocation; + gint size, pos; + + gtk_widget_get_allocation (GTK_WIDGET (range), &allocation); + + if (range->priv->orientation == GTK_ORIENTATION_VERTICAL) + { + size = allocation.height; + pos = range->priv->mouse_y; + } + else + { + size = allocation.width; + pos = range->priv->mouse_x; + } + + if (pos < 0) + mode = GTK_SCROLL_STEP_BACKWARD; + else if (pos > size) + mode = GTK_SCROLL_STEP_FORWARD; + } + + if (mode != range->priv->autoscroll_mode) + { + remove_autoscroll (range); + range->priv->autoscroll_mode = mode; + add_autoscroll (range); + } +} + static gboolean gtk_range_motion_notify (GtkWidget *widget, GdkEventMotion *event) @@ -2982,7 +3086,9 @@ gtk_range_motion_notify (GtkWidget *widget, if (!priv->zoom_set && priv->press_and_hold != NULL) gtk_press_and_hold_process_event (priv->press_and_hold, (GdkEvent *)event); - update_slider_position (range, event->x, event->y); + update_autoscroll_mode (range); + if (priv->autoscroll_mode == GTK_SCROLL_NONE) + update_slider_position (range, event->x, event->y); } /* We handled the event if the mouse was in the range_rect */ -- 2.30.2